#include <config.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/asm.h>

#include "ns16550.h"
#include "i2c.h"

#include "mm/2H_chip_define.h"
#include "mm/ddr_param_define.h"
#include "mm/ddr_config_define.h"

#define msize		s2

#define Index_Store_Tag_D	0x09
#define Index_Store_Tag_S	0x0B
#define Index_Store_Tag_I	0x08

	.set   mips64
FEXPORT(lowlevel_init)
	.set noreorder
	move fp, ra

#if 1
	move   s0, zero

	mfc0   t0, CP0_STATUS
	li     t1, 0x64000000|ST0_KX|ST0_SX|ST0_UX|ST0_BEV      # {cu3,cu2,cu1,cu0}<={0110, status_fr<=1,0xe0 to enable 64bit space
	or     t0, t0, t1
	mtc0   t0, CP0_STATUS
	mtc0   zero, CP0_CAUSE
#endif

	/* spi speedup */
	li   v0, 0xbfe70000
	li   v1, 0xff
	sb   v1, 0x5(v0) //set all spi cs to 1, default input
	li   v1, 0x47
	sb   v1, 0x4(v0) //spi speedup
	li   v1, 0x01
	sb   v1, 0x6(v0)

//	move s0, zero

#include "pllcfg.S"

	li  a0, (SBC_CLOCK_RATE/16)/CONFIG_BAUDRATE
	bal initserial
	nop
	PRINTSTR("\r\nserial init ok\r\n")
	nop

	//m0 win2 mmap ?
	li t0, 0xbfd80090
	li t1, 0x82
	sw t1, 0x0(t0)
	li t1, 0x10000084
	sw t1, 0x8(t0)

#ifndef LS2H_3A
	/* This window makes sys more stable, be carefull to remove it */
	li   a0, 0xbfd800a8
	dli  t1, 0x40000084
	sd   t1, 0x0(a0)

#define set_L2_win(mas_i, win_i, base, mask, mmap) \
	li      t0, 0xbfd80000 ;	\
	addiu   t0, t0, mas_i*0x100 ;	\
	addiu   t0, t0, win_i*0x8 ;	\
	dli     t1, base ;		\
	sd      t1, 0x0(t0) ;		\
	dli     t1, mask ;		\
	sd      t1, 0x40(t0) ;		\
	dli     t1, mmap ;		\
	sd      t1, 0x80(t0)

#define set_L1_mas6(win_i, base, mask, mmap) \
	li      t0, 0xbfd82600 ;	\
	addiu   t0, t0, win_i*0x8 ;	\
	dli     t1, base ;		\
	sd      t1, 0x0(t0) ;		\
	dli     t1, mask ;		\
	sd      t1, 0x40(t0) ;		\
	dli     t1, mmap ;		\
	sd      t1, 0x80(t0)

	#IODMA 4G
	# DMA address		   CPU phy address
	# 0x00000000 ~ 0x0fffffff  ---> 0x00000000 ~ 0x010000000
	# 0x00000000 ~ 0x3fffffff  ---> 0x10000000 ~ 0x13fffffff
	# 0x40000000 ~ 0x7fffffff  ---> 0x14000000 ~ 0x17fffffff
	# 0x80000000 ~ 0xbfffffff  ---> 0x18000000 ~ 0x1bfffffff
	# 0xc0000000 ~ 0xffffffff  ---> 0x1c000000 ~ 0x1ffffffff

	# sbridge
	set_L2_win(1, 0, 0x0000000000000000, 0xfffffffff0000000, 0x00000000400000f3) # lower 256M
	set_L2_win(1, 1, 0x0000000000000000, 0xffffffffc0000000, 0x00000001400000f3) # 0~1G
	set_L2_win(1, 2, 0x0000000040000000, 0xffffffffc0000000, 0x00000002400000f3) # 1~2G
	set_L2_win(1, 3, 0x0000000080000000, 0xffffffffc0000000, 0x00000003400000f3) # 2~3G
	set_L2_win(1, 4, 0x00000000c0000000, 0xffffffffc0000000, 0x00000004400000f3) # 3~4G
	set_L2_win(1, 5, 0xffffffff80000000, 0xffffffffc0000000, 0x00000003400000f3) # 2~3G for sata/usb
	set_L2_win(1, 6, 0xffffffffc0000000, 0xffffffffc0000000, 0x00000004400000f3) # 3~4G for sata/usb

	# pcie
	set_L2_win(4, 0, 0x0000000000000000, 0xfffffffffffff000, 0x000000001fd00081) # lower 256M
#if 0
	set_L2_win(4, 1, 0x0000000000000000, 0xffffffff00000000, 0x00000000000000f0) # for uncache dma
#else
	set_L2_win(4, 1, 0x0000000000000000, 0xfffffffff0000000, 0x00000000400000f3) # lower 256M
#endif
	set_L2_win(4, 2, 0x0000000000000000, 0xffffffffc0000000, 0x00000001400000f3) # 0~1G
	set_L2_win(4, 3, 0x0000000040000000, 0xffffffffc0000000, 0x00000002400000f3) # 1~2G
	set_L2_win(4, 4, 0x0000000080000000, 0xffffffffc0000000, 0x00000003400000f3) # 2~3G
	set_L2_win(4, 5, 0x00000000c0000000, 0xffffffffc0000000, 0x00000004400000f3) # 3~4G
	set_L2_win(4, 6, 0xffffffff80000000, 0xffffffffc0000000, 0x00000003400000f3) # 2~3G for sata/usb
	set_L2_win(4, 7, 0xffffffffc0000000, 0xffffffffc0000000, 0x00000004400000f3) # 3~4G for sata/usb

	# gpu
	set_L2_win(2, 0, 0x0000000000000000, 0xfffffffff0000000, 0x00000000400000f3) # lower 256M
	set_L2_win(2, 1, 0x0000000000000000, 0xffffffffc0000000, 0x00000001400000f3) # 0~1G
	set_L2_win(2, 2, 0x0000000040000000, 0xffffffffc0000000, 0x00000002400000f3) # 1~2G
	set_L2_win(2, 3, 0x0000000080000000, 0xffffffffc0000000, 0x00000003400000f3) # 2~3G
	set_L2_win(2, 4, 0x00000000c0000000, 0xffffffffc0000000, 0x00000004400000f3) # 3~4G
	set_L2_win(2, 5, 0xffffffff80000000, 0xffffffffc0000000, 0x00000003400000f3) # 2~3G for sata/usb
	set_L2_win(2, 6, 0xffffffffc0000000, 0xffffffffc0000000, 0x00000004400000f3) # 3~4G for sata/usb

	set_L1_mas6(0, 0x0000000040000000, 0xffffffffc0000000, 0x00000000000000f0)
	set_L1_mas6(1, 0x0000000140000000, 0xffffffffc0000000, 0x00000001000000f0)
	set_L1_mas6(2, 0x0000000240000000, 0xffffffffc0000000, 0x00000001400000f0)
	set_L1_mas6(3, 0x0000000340000000, 0xffffffffc0000000, 0x00000001800000f0)
	set_L1_mas6(4, 0x0000000440000000, 0xffffffffc0000000, 0x00000001c00000f0)
	set_L1_mas6(5, 0x0000000000000000, 0x0000000000000000, 0x00000000000000f0) # others, all to L2$

#else
	li	v0, 0xbfd00044
	li	v1, 0xff7fffff
	sw	v1, (v0)
#endif

	TTYDBG("\r\nInit Memory begin, wait a while......\r\n")
#ifdef AUTO_DDR_CONFIG
 #ifdef DIMM_ID
	dli   s1, 0xff000004 + (DIMM_ID << 16)  //set use MC1 or MC0 or MC1/0 and give All device id
 #else
	dli   s1, 0xff500004  //set use MC1 or MC0 or MC1/0 and give All device id
 #endif
#else
#ifdef DDR3_SODIMM
//	dli   s1, 0x00000000c0a10404
	dli   s1, ( MC_SDRAM_TYPE_DDR3    /* sdram type: DDR3/DDR2 */ \
		| MC_DIMM_ECC_NO        /* dimm ECC: YES/NO */ \
		| MC_DIMM_BUF_REG_NO    /* dimm buffer register: YES/NO, for RDIMM use YES, all else use NO*/ \
		| MC_DIMM_WIDTH_64      /* memory data width: 64/32 */ \
		| MC_SDRAM_ROW_15       /* sdram row address number: 15~11 */ \
		| MC_SDRAM_COL_10       /* sdram column address number: 12~9 */ \
		| MC_SDRAM_BANK_8       /* sdram bank number: 8/4 */ \
		| MC_ADDR_MIRROR_NO     /* for standard DDR3 UDIMM, use YES, else use NO */ \
		| MC_SDRAM_WIDTH_X8     /* SDRAM device data width: 8/16 */ \
		| MC_USE_CS_0           /* the CS pins the sdram connected on(split by '_', from small to big) */ \
		| MC_MEMSIZE_(4)        /* MC memory size, unit: 512MB */ \
		)
	or    s1, s1, 0x4
#elif DDR3_SMT
//	dli   s1, 0x00000000c0a18404
	dli   s1, ( MC_SDRAM_TYPE_DDR3    /* sdram type: DDR3/DDR2 */ \
		| MC_DIMM_ECC_NO        /* dimm ECC: YES/NO */ \
		| MC_DIMM_BUF_REG_NO    /* dimm buffer register: YES/NO, for RDIMM use YES, all else use NO*/ \
		| MC_DIMM_WIDTH_64      /* memory data width: 64/32 */ \
		| MC_SDRAM_ROW_15       /* sdram row address number: 15~11 */ \
		| MC_SDRAM_COL_10       /* sdram column address number: 12~9 */ \
		| MC_SDRAM_BANK_8       /* sdram bank number: 8/4 */ \
		| MC_ADDR_MIRROR_NO     /* for standard DDR3 UDIMM, use YES, else use NO */ \
		| MC_SDRAM_WIDTH_X16    /* SDRAM device data width: 8/16 */ \
		| MC_USE_CS_0           /* the CS pins the sdram connected on(split by '_', from small to big) */ \
		| MC_MEMSIZE_(4)        /* MC memory size, unit: 512MB */ \
		)
	or    s1, s1, 0x4
#else
//	dli   s1, 0x00000000c1e40204  //dian mian 1GB
//	dli   s1, 0x00000000c1ec0404  //shuang mian 2GB
//	dli   s1, 0x00000000c9ec0204  //shuang mian 1GB(32 bit DIMM)

	dli   s1, ( MC_SDRAM_TYPE_DDR3    /* sdram type: DDR3/DDR2 */ \
		| MC_DIMM_ECC_NO        /* dimm ECC: YES/NO */ \
		| MC_DIMM_BUF_REG_NO    /* dimm buffer register: YES/NO, for RDIMM use YES, all else use NO*/ \
		| MC_DIMM_WIDTH_32      /* memory data width: 64/32 */ \
		| MC_SDRAM_ROW_14       /* sdram row address number: 15~11 */ \
		| MC_SDRAM_COL_10       /* sdram column address number: 12~9 */ \
		| MC_SDRAM_BANK_8       /* sdram bank number: 8/4 */ \
		| MC_ADDR_MIRROR_YES    /* for standard DDR3 UDIMM, use YES, else use NO */ \
		| MC_SDRAM_WIDTH_X8     /* SDRAM device data width: 8/16 */ \
		| MC_USE_CS_2_3         /* the CS pins the sdram connected on(split by '_', from small to big) */ \
		| MC_MEMSIZE_(2)        /* MC memory size, unit: 512MB */ \
		)
	or    s1, s1, 0x4
#ifdef DDR_S1
	dli	s1, DDR_S1
#endif
#endif
#endif

	PRINTSTR("\r\nInitialize the ls2h IIC controller\r\n")
	bal   ls2h_i2c_init
	nop
#if 1
do_caches:
	TTYDBG("godson2 caches found\r\n")
	bal   godson2_cache_init
	nop
	TTYDBG("scache init\r\n")
	bal   scache_init // smh
	nop

/* enable kseg0 cachability */
	mfc0   a0, CP0_CONFIG
#if 1
	and    a0, a0, 0xfffffff8
	or     a0, a0, 0x3           // ENABLE
#else
	and    a0, a0, 0xfffffff8
	or     a0, a0, 0x2           // DISABLE
#endif
	mtc0   a0, CP0_CONFIG

/* jmp to 0x9fc... */
#if 0
	lui     t0, 0xdfff
	ori     t0, t0, 0xffff
	bal     1f
	nop
1:
	and     ra, ra, t0
	addiu   ra, ra, 16
	jr      ra
	nop
#endif
	PRINTSTR("cache enable done\r\n")
#endif

//DDR config start
#define DISABLE_DIMM_ECC
#ifndef ARB_LEVEL
//#define FIX_DDR_PARAM
#endif

#ifdef ARB_LEVEL
#define AUTO_ARB_LEVEL
#endif

#ifdef AUTO_ARB_LEVEL
#define CHECK_ARB_LEVEL_FREQ
#ifdef AUTO_DDR_CONFIG
#define CHECK_ARB_LEVEL_DIMM
#endif
#if defined(FASTDDRBOOT)
#undef CHECK_ARB_LEVEL_DIMM
#endif
//#define DEBUG_AUTO_ARB_LEVEL
#endif
//#define DEBUG_DDR
//#define DEBUG_DDR_PARAM

    move    msize, $0
    move    s3, $0

//!!!!important--s1 must be correctly set

    TTYDBG("NODE 0 MEMORY CONFIG BEGIN\r\n")
#include "mm/loongson2H_ddr_config.S"

	/*judge the node0 whether have memory*/
	move    a0, msize

#ifdef AUTO_ARB_LEVEL
#include "mm/store_auto_arb_level_info.S"
#endif

#ifdef AUTO_DDR_CONFIG
//	bal  spd_info_store
//	nop
#endif

	jr fp
	nop
	.set reorder

LEAF(initserial)
.set noat
	move AT, ra

	li   v0, UART_BASE_ADDR
	li   v1, FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_4
	sb   v1, NSREG(NS16550_FIFO)(v0)
	li   v1, CFCR_DLAB                  #DLAB
	sb   v1, NSREG(NS16550_CFCR)(v0)
	sb   a0, NSREG(NS16550_DATA)(v0)
	srl  a0, 8
	sb   a0, NSREG(NS16550_IER)(v0)     #set BRDH
	li   v1, CFCR_8BITS                 #8bit
	sb   v1, NSREG(NS16550_CFCR)(v0)
	li   v1, MCR_DTR|MCR_RTS
	sb   v1, NSREG(NS16550_MCR)(v0)
	li   v1, 0x0
	sb   v1, NSREG(NS16550_IER)(v0)

	jr   ra
	nop
.set at
END(initserial)

LEAF(printk)
	.set noreorder
	move  a2, ra
	move  a1, a0
	lbu   a0, 0(a1)
1:
	beqz  a0, 2f
	nop
	bal   tgt_putchar
	nop
	addiu a1, 1
	lbu   a0, 0(a1)
	b     1b
	nop
2:
	jr    a2
	nop
	.set reorder
END(printk)

tgt_putchar:
	la   v0, UART_BASE_ADDR
1:
	lbu  v1, NSREG(NS16550_LSR)(v0)
	and  v1, LSR_TXRDY
	beqz v1, 1b
	nop

	sb   a0, NSREG(NS16550_DATA)(v0)
	jr   ra
	nop

	.rdata
hexchar:
	.ascii	"0123456789abcdef"
	.text

#ifdef PRINT_MSG
LEAF(hexserial)
	move  a2, ra
	move  a1, a0
	li    a3, 8
1:
	rol   a0, a1, 4
	move  a1, a0
	and   a0, 0xf
	la    t0, hexchar
	addu  t0, s0
	addu  t0, a0
	lbu   a0, 0(t0)
	bal   tgt_putchar

	addu  a3, -1
	bnez  a3, 1b
	nop

	jr    a2
	nop
END(hexserial)
#else
LEAF(hexserial)
	move  a2, ra
	jr    a2
	nop
END(hexserial)
#endif

LEAF(hexserial64)
	move	t7, ra
	jr	t7
   nop
END(hexserial64)

LEAF(godson2_cache_init)
	.set noreorder
	####part 2####
cache_detect_4way:
	mfc0    t4, CP0_CONFIG
	andi    t5, t4, 0x0e00
	srl     t5, t5, 9
	andi    t6, t4, 0x01c0
	srl     t6, t6, 6
	addiu   t6, t6, 10      #4way
	addiu   t5, t5, 10      #4way
	addiu   t4, $0, 1
	sllv    t6, t4, t6
	sllv    t5, t4, t5
	addiu   t7, $0, 4
	####part 3####
	lui     a0, 0x8000
#	addu    a1, $0, t5
#	addu    a2, $0, t6
	li      a1, (1<<14) #64k/4way
	li      a2, (1<<14)
cache_init_d4way:
//a0=0x80000000, a1=icache_size, a2=dcache_size
//a3, v0 and v1 used as local registers
	mtc0    $0, CP0_TAGHI
	li      t0, 0x22
	mtc0    t0, CP0_ECC
	addu    v0, $0, a0
	addu    v1, a0, a2
1:
	slt     a3, v0, v1
	beq     a3, $0, 1f
	nop
	mtc0    $0, CP0_TAGLO
	cache   Index_Store_Tag_D, 0x0(v0)
	cache   Index_Store_Tag_D, 0x1(v0)
	cache   Index_Store_Tag_D, 0x2(v0)
	cache   Index_Store_Tag_D, 0x3(v0)
	beq     $0, $0, 1b
	addiu   v0, v0, 0x20
1:
cache_flush_i4way:
	addu    v0, $0, a0
	addu    v1, a0, a1
	mtc0    $0, CP0_TAGLO
	mtc0    $0, CP0_TAGHI
	mtc0    $0, CP0_ECC
1:
	slt     a3, v0, v1
	beq     a3, $0, 1f
	nop
	cache   0x08, 0x0(v0)/*Index_Store_Tag_I*/
	cache   0x08, 0x1(v0)/*Index_Store_Tag_I*/
	cache   0x08, 0x2(v0)/*Index_Store_Tag_I*/
	cache   0x08, 0x3(v0)/*Index_Store_Tag_I*/
	beq     $0, $0, 1b
	addiu   v0, v0, 0x20
1:
cache_init_finish:
	jr      ra
	nop
cache_init_panic:
	TTYDBG("\r\ncache init panic\r\n")
1:
	b       1b
	nop
END(godson2_cache_init)

LEAF(scache_init)
	move    t7, ra
	lui     a0, 0x8000
	lui     a2, 0x0004      #1M/4way
scache_init_4way:
//a0=0x80000000, a2=scache_size
//a3, v0 and v1 used as local registers
	li      t0, 0x22
	mtc0    t0, CP0_ECC
	mtc0    $0, CP0_TAGHI
	mtc0    $0, CP0_TAGLO
	addu    v0, $0, a0
	addu    v1, a0, a2
1:
	slt     a3, v0, v1
	beq     a3, $0, 1f
	nop
	cache   Index_Store_Tag_S, 0x0(v0)
	cache   Index_Store_Tag_S, 0x1(v0)
	cache   Index_Store_Tag_S, 0x2(v0)
	cache   Index_Store_Tag_S, 0x3(v0)
	beq     $0, $0, 1b
	addiu   v0, v0, 0x20
1:
scache_init_finish:
	jr      t7
	nop
scache_init_panic:
	TTYDBG("\r\nscache init panic\r\n")
1:
	b       1b
   nop
END(scache_init)

LEAF(spd_info_store)
	move    t8, ra

	TTYDBG("\r\n spd_info_store begin.\r\n")

	dli    t5, 0xffffffff8fffa000;

	dli    t7, 0xa9
	dli    t6, 0xad

	move   a0, t5
	daddiu a1, a0, 0x200
1:
	sb     zero, 0(a0)
	daddiu a0, 1
	bltu   a0, a1, 1b
	nop
4:
	move   a0, t7
	dli    a1, 0x2
	//GET_I2C_NODE_ID_a2
	bal    i2cread
	nop
	dli    t3, 0x80
	bltu   v0, t3, 2f
	nop
	move   t3, t5
	daddiu t3, 0x100;
	move   t4, t5
1:
	sb     zero, 0(t4)
	daddiu t4, 0x1
	bltu   t4, t3, 1b
	nop
	b      3f
	nop
2:
	move   t4, t5
	dli    t0, 0x0 //used as counter
1:
	move   a0, t7
	move   a1, t0
	//GET_I2C_NODE_ID_a2
	bal    i2cread
	nop
	sb     v0, 0(t4)
	dli    a1, 0x100
	daddiu t4, 0x1
	daddiu t0, 0x1
	bne    t0, a1, 1b
	nop
3:
	daddiu t5, 0x100
	daddiu t7, 0x2
	bltu   t7, t6, 4b
	nop

	TTYDBG("\r\n spd_info_store done.\r\n")
	jr     t8
	nop
END(spd_info_store)

LEAF(ls2h_i2c_init)
#ifdef I2C_CONTROL1
	li	v0, LS2H_I2C1_CTR_REG
	li	v1, 0x0
	sb	v1, 0x0(v0)
	li	v0, LS2H_I2C1_PRER_LO_REG
	li	v1, 0x2c
	sb	v1, 0x0(v0)
	li	v0, LS2H_I2C1_PRER_HI_REG
	li	v1, 0x1
	sb	v1, 0x0(v0)
	li	v0, LS2H_I2C1_CTR_REG
	li	v1, 0x80
	sb	v1, 0x0(v0)
	jr	ra
	nop
#else
	li	v0, LS2H_I2C0_CTR_REG
	li	v1, 0x0
	sb	v1, 0x0(v0)
	li	v0, LS2H_I2C0_PRER_LO_REG
	li	v1, 0x2c
	sb	v1, 0x0(v0)
	li	v0, LS2H_I2C0_PRER_HI_REG
	li	v1, 0x1
	sb	v1, 0x0(v0)
	li	v0, LS2H_I2C0_CTR_REG
	li	v1, 0x80
	sb	v1, 0x0(v0)
	jr	ra
	nop
#endif
END(ls2h_i2c_init)

#include "i2c.S"

#ifdef SET_DDR_FREQ
#include "mm/2H_ddr_pll_config.S"
#endif

#ifdef AUTO_DDR_CONFIG
#include "mm/detect_node_dimm.S"
#endif

#include "mm/2H_ddr_config.S"

#ifdef ARB_LEVEL
//#define USE_SPECIAL_WRLVL_DQ_DELAY
#ifdef USE_SPECIAL_WRLVL_DQ_DELAY
#define WRLVL_DQ_SPECIAL_DLY 0x2424242424242424 //specific the wrlvl_dq_delay for each byte lane
#endif
#include "mm/ARB_level_new.S"
#endif

#ifdef DEBUG_DDR
//#include "mm/Test_Mem.S"
#endif

    .text
    .global ddr2_reg_data
    .global ddr3_reg_data
    .align  5

#ifdef LS2H_MODULE_03
#include "mm/loongson3A3_ddr_param-mt.S"
#else
#include "mm/loongson3A3_ddr_param.400.S"
#endif

#ifdef ARB_LEVEL
    .text
    .global c0_mc0_level_info
    .global c0_mc1_level_info

#include "mm/loongson3A3_ddr_param.lvled.S"
#else
#ifdef FIX_DDR_PARAM
#include "mm/loongson3A3_ddr_param.fix.S"
#endif
#endif

